using UnityEngine;
using System;
using System.Xml;
using System.Collections.Generic;
using EventBehaviorTree;
using BuffSystem;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public class AssetsManager : MonoBehaviour
{
    public static AssetsManager AssetsManagerInstance { get; set; }
    public Dictionary<string, BaseProperty> _cruiser_Dic = new Dictionary<string, BaseProperty>();
    public Dictionary<string, WeaponProperty> _weapon_Dic = new Dictionary<string, WeaponProperty>();
    //public Dictionary<string, Buff> _buff_Dic = new Dictionary<string, Buff>();

    void Awake()
    {
        if (AssetsManagerInstance != null)
        {
            Destroy(AssetsManagerInstance);
            return;
        }
        AssetsManagerInstance = this;
    }

    void Start()
    {
        Gen_Assets();
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.B)) { Gen_BuffAssets(); }
    }

    private void Gen_Assets()
    {
        XmlDocument XMLDoc = new XmlDocument();
        XMLDoc.Load(Application.dataPath + "/StreamingAssets/Assets.xml");//relative path of XML file
        #region Create cruiser
        XmlNodeList xnListCruiser = XMLDoc.SelectNodes("/Root/Cruiser");
        foreach (XmlNode xnCruiser in xnListCruiser)
        {
            _cruiser_Dic.Add(xnCruiser.Attributes[0].Value,
                new BaseProperty(
                    float.Parse(xnCruiser.Attributes[1].Value),
                    float.Parse(xnCruiser.Attributes[2].Value),
                    float.Parse(xnCruiser.Attributes[3].Value),
                    float.Parse(xnCruiser.Attributes[4].Value)));
        }
        #endregion
        #region Create weapon
        XmlNodeList xnListWeapon = XMLDoc.SelectNodes("/Root/Weapon");
        foreach (XmlNode xnWeapon in xnListWeapon)
        {
            _weapon_Dic.Add(xnWeapon.Attributes[0].Value,
                new WeaponProperty(
                    float.Parse(xnWeapon.Attributes[1].Value),
                    float.Parse(xnWeapon.Attributes[2].Value)));
        }
        #endregion
    }

    private void Gen_BuffAssets()
    {
        XmlDocument XMLDoc = new XmlDocument();
        string xmlPath = Application.dataPath + "/StreamingAssets/Assets.xml";//relative path of XML file
        XMLDoc.Load(xmlPath);
        XmlNodeList xnListBuff = XMLDoc.SelectNodes("/Root/Buff");
        Dictionary<string, Buff> buffDic = new Dictionary<string, Buff>();
        foreach (XmlNode xnBuffNode in xnListBuff)
        {
            Buff buff = new Buff(xnBuffNode.Attributes[XMLAttri.BuffName].Value);
            buff.SetRootNode(Gen_XmlBTNode(xnBuffNode, buff));
            buffDic.Add(buff._name.ToString(), buff);
        }
        SerializeBuff(buffDic);
    }
    private Container Gen_XmlBTNode(XmlNode xnBuffNode, Buff buff)
    {
        return Gen_XmlBTNode(xnBuffNode.FirstChild, buff, null, true, xnBuffNode.Attributes["buffName"].Value);
    }
    private Container Gen_XmlBTNode(XmlNode xnFunction, Buff buff, Container parentNode)
    {
        return Gen_XmlBTNode(xnFunction, buff, parentNode, false, null);
    }
    private Container Gen_XmlBTNode(XmlNode xnFunction, Buff buff, Container parentNode, bool isRootNode, string key)
    {
        NodeType nodeType = (NodeType)Enum.Parse(typeof(NodeType), xnFunction.Name);
        EventNode BTNode = new EventNode();
        switch (nodeType)//generate node of nodeType
        {
            case NodeType.Sequencer:
                BTNode = new Sequencer();
                break;
            case NodeType.Selector:
                BTNode = new Selector();
                break;
            case NodeType.TimerSequencer:
                BTNode = new TimerSequencer(xnFunction.Attributes[XMLAttri.Pattern].Value, xnFunction.Attributes[XMLAttri.Duration].Value);
                break;
            case NodeType.ConditionSequencer:
                BTNode = xnFunction.Attributes[XMLAttri.Duration] == null ? new ConditionSequencer() : new ConditionSequencer(xnFunction.Attributes[XMLAttri.Duration].Value);
                break;
            case NodeType.TaskNode:
                BTNode = xnFunction.Attributes[XMLAttri.Pattern] == null ? new TaskNode() : new TaskNode(xnFunction.Attributes[XMLAttri.Pattern].Value);
                break;
            default:
                break;
        }
        if (!isRootNode) { parentNode.AddSubNodes(BTNode); }
        else { ((Container)BTNode).SetKey(key); }//rootNode
        switch (nodeType)
        {
            case NodeType.Sequencer:
            case NodeType.Selector:
            case NodeType.TimerSequencer:
                foreach (XmlNode child in xnFunction.ChildNodes) { Gen_XmlBTNode(child, buff, (Container)BTNode); }
                break;
            case NodeType.ConditionSequencer:
                ((ConditionSequencer)BTNode).SetNodeFunction((ICondition)Gen_Component(xnFunction, buff, BTNode._key));
                foreach (XmlNode child in xnFunction.ChildNodes) { Gen_XmlBTNode(child, buff, (Container)BTNode); }
                break;
            case NodeType.TaskNode:
                ((TaskNode)BTNode).SetNodeFunction((IAction)Gen_Component(xnFunction, buff, BTNode._key));
                break;
            default:
                break;
        }
        return !isRootNode ? null : (Container)BTNode;
    }
    private FunctionComponent Gen_Component(XmlNode xmlNode, Buff buff, string nodeKey)
    {
        XmlNode occupantNode = xmlNode.FirstChild;//the xmlNode carries a component, and may be null
        switch ((ComponentType)Enum.Parse(typeof(ComponentType), xmlNode.Attributes[XMLAttri.ComponentType].Value))
        {
            case ComponentType.Unregister:
                return buff.AddComponent(new Unregister(xmlNode.Attributes[XMLAttri.OccupantKey].Value, xmlNode.Attributes[XMLAttri.OccupantType].Value));
            case ComponentType.Register:
                return buff.AddComponent(SetOccupant(
                    new Register(occupantNode != null ? nodeKey : xmlNode.Attributes[XMLAttri.OccupantKey].Value),
                    occupantNode != null ? occupantNode.Name : OccupantType.Buff.ToString(), occupantNode));
            case ComponentType.TagCondition:
                return buff.AddComponent(new TagCondition(new TagSystem(occupantNode.Attributes[XMLAttri.Tag].Value)));
            case ComponentType.StackCounter:
                return buff.AddComponent(new StackCounter(nodeKey));
            case ComponentType.TriggerEvent:
                TriggerEvent triggerEvent = new TriggerEvent(nodeKey, xmlNode.Attributes[XMLAttri.TriggerType].Value);
                return buff.AddComponent(triggerEvent);
            default:
                return null;
        }
        Register SetOccupant(Register register, string occupantTypeString, XmlNode occupantNode)
        {
            OccupantType occupantType = (OccupantType)Enum.Parse(typeof(OccupantType), occupantTypeString);
            switch (occupantType)
            {
                case OccupantType.PropertyModifier:
                    register.SetOccupant(new PropertyModifier(occupantNode.Attributes));
                    break;
                case OccupantType.Tag:
                    register.SetOccupant(new TagSystem(occupantNode.Attributes[XMLAttri.Tag].Value));
                    break;
                default:
                    break;
            }
            register.SetOccupantType(occupantType);
            return register;
        }
    }
    private void SerializeBuff(Dictionary<string, Buff> buffDic)
    {
        Debug.Log(Application.persistentDataPath);
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        if (!Directory.Exists(Application.persistentDataPath + "/BuffData"))
        {
            Directory.CreateDirectory(Application.persistentDataPath + " /BuffData");
        }
        foreach (Buff buff in buffDic.Values)
        {
            FileStream fileStream = File.Create(Application.persistentDataPath + " /BuffData/" + buff._name + ".buff");
            binaryFormatter.Serialize(fileStream, buff);
            fileStream.Close();
        }
    }
    //private void DeserializeBuff()
    //{
    //    BinaryFormatter binaryFormatter = new BinaryFormatter();
    //    _buff_Dic = new Dictionary<string, Buff>();
    //    if (Directory.Exists(Application.persistentDataPath + "/BuffData"))
    //    {
    //        FileInfo[] fileInfos = new DirectoryInfo(Application.persistentDataPath + "/BuffData").GetFiles();
    //        foreach (FileInfo file in fileInfos)
    //        {
    //            FileStream fileStream = file.Open(FileMode.Open);
    //            Buff buff = binaryFormatter.Deserialize(fileStream) as Buff;
    //            _buff_Dic.Add(buff._name, buff);
    //            fileStream.Close();
    //        }
    //    }
    //    Debug.Log("Deserialized");
    //}
    public Buff DeserializeBuff(string buffName)
    {
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        string path = Application.persistentDataPath + "/BuffData/" + buffName + ".buff";
        if (File.Exists(path))
        {
            FileStream fileStream = new FileInfo(path).Open(FileMode.Open);
            Buff buff = binaryFormatter.Deserialize(fileStream) as Buff;
            fileStream.Close();
            return buff;
        }
        return null;
    }
}
